using System;
using ZoneFiveSoftware.Common.Data;
using ZoneFiveSoftware.Common.Data.Fitness;
using FilteredStatistics.Controller;
using System.Collections.Generic;

namespace FilteredStatistics.Data
{
    class FilterResults : List<IValueRange<DateTime>>
    {
        public FilterResults()
        {
        }

        public FilterResults(IValueRangeSeries<DateTime> valueRangeSeries)
        {
            AddRange(valueRangeSeries);
        }

        public void AddRange(IValueRangeSeries<DateTime> valueRangeSeries)
        {
            foreach (IValueRange<DateTime> currentRange in valueRangeSeries)
            {
                base.Add(currentRange);
            }
        }

        public static implicit operator ValueRangeSeries<DateTime>(FilterResults resultList)
        {
            ValueRangeSeries<DateTime> result = new ValueRangeSeries<DateTime>();

            foreach(ValueRange<DateTime> value in resultList)
            {
                result.Add(value);
            }

            return result;
        }

        public new int IndexOf(IValueRange<DateTime> item)
        {
            // Overloaded since it compares pointers, not values
            for (int i = 0; i < base.Count; ++i)
            {
                if (item.Lower == this[i].Lower &&
                    item.Upper == this[i].Upper)
                {
                    return i;
                }
            }

            return -1;
        }

        public bool RangesAreConsecutive(List<ValueRange<DateTime>> ranges)
        {
            if (ranges.Count > 1)
            {
                int firstRangeIndex = IndexOf(ranges[0]);
                int lastRangeIndex = IndexOf(ranges[ranges.Count - 1]);

                // Check the difference between first and last index and make sure it matches
                //  our selection count. We can do this as the results are ordered
                return (lastRangeIndex - firstRangeIndex + 1) == ranges.Count;
            }

            return false;
        }

        public bool MergeResults(List<ValueRange<DateTime>> rangesToMerge)
        {
            if (RangesAreConsecutive(rangesToMerge))
            {
                DateTime lowerLimit = rangesToMerge[0].Lower;
                DateTime upperLimit = rangesToMerge[rangesToMerge.Count - 1].Upper;

                // Remove ranges in results
                foreach (IValueRange<DateTime> currentRange in rangesToMerge)
                {
                    base.Remove(currentRange);
                }

                // Add new range with the right limits
                base.Add(new ValueRange<DateTime>(lowerLimit, upperLimit));

                return true;
            }

            return false;
        }

        public void InvertResults(IActivity referenceActivity)
        {
            if (base.Count > 0)
            {
                ActivityInfo activityInfo = ActivityInfoCache.Instance.GetInfo(referenceActivity);
                DateTime activityStartTime = activityInfo.ActualTrackStart;
                DateTime activityEndTime = activityStartTime + Utils.GetActiveActivityTime(referenceActivity);
                ValueRangeSeries<DateTime> invertedResult = new ValueRangeSeries<DateTime>();

                // Check if our inversion starts at the activity's beginning
                if (this[0].Lower != activityStartTime)
                {
                    // We must start a range here
                    invertedResult.Add(new ValueRange<DateTime>(activityStartTime, this[0].Lower));
                }

                // Go through our existing result, adding the inverted times
                for (int i = 0; i < this.Count - 1; ++i)
                {
                    // We must start a range here
                    invertedResult.Add(new ValueRange<DateTime>(this[i].Upper, this[i + 1].Lower));
                }

                // Check if our inversion ends at the activitry's end
                if (this[this.Count - 1].Upper != activityEndTime)
                {
                    // We must start a range here
                    invertedResult.Add(new ValueRange<DateTime>(this[this.Count - 1].Upper, activityEndTime));
                }

                base.Clear();
                AddRange(invertedResult);
            }
            else
            {
                AddRange(WholeActivityFilteredDataSet.Instance.GetFilteredTimes());
            }
        }
    }
}
